#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "stdarg.h"
#include "math.h"
#include "pthread.h"

#include "BList.h"

#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"

#include "CaseBaseUtils.h"

#include "AWLVP_DistanceMatrix.h"


/*

	List<FeatureTerm> m_projection_cases;
	List<FeatureTerm> m_projection_solutions;
	float *m_projection_matrix;
	
	float m_progress;
	bool m_finished;

*/ 

NOOSDistanceMatrix::NOOSDistanceMatrix(FTKBase *cb,Sort *s)
{
	List<FeatureTerm> *l;
	FeatureTerm *f;

	m_sort=s;

	l=cb->SearchFT(s);
	while(!l->EmptyP()) {
		f=l->ExtractIni();
		m_projection_cases.Add(new FeatureTerm(f));
		m_projection_descriptions.Add(get_description(f));
		m_projection_solutions.Add(get_solution(f));
	} /* while */ 
	delete l;

	m_projection_matrix=new float[m_projection_cases.Length()*m_projection_cases.Length()];
	m_progress=0.0;
	m_finished=false;

	/* Start thread: */ 
	{
		int retval;
		pthread_attr_init(&m_thread_attr);

		retval=pthread_create(&m_thread, &m_thread_attr, launch_thread, (void *)this);
//		write_debug_report("AW_ThreadedBrain [%p]: thread launch retval: %i\n",this,retval);
	}

} /* NOOSDistanceMatrix::NOOSDistanceMatrix */ 


NOOSDistanceMatrix::~NOOSDistanceMatrix(void)
{
	/* Stop thread: */ 
	pthread_cancel(m_thread);
	pthread_join(m_thread,0);

	delete []m_projection_matrix;
	m_projection_matrix=0;
} /* NOOSDistanceMatrix::~NOOSDistanceMatrix */ 


bool NOOSDistanceMatrix::need_to_recompute(FTKBase *cb,Sort *s)
{
	List<FeatureTerm> *l;
	FeatureTerm *f;

	m_sort=s;

	l=cb->SearchFT(s);

	if (l->Length()!=m_projection_cases.Length()) {
		l->ExtractAll();
		delete l;
		return true;
	} /* if */  

	while(!l->EmptyP()) {
		f=l->ExtractIni();
		if (m_projection_cases.Position(f)==-1) {
			l->ExtractAll();
			delete l;
			return true;
		} /* if */ 
	} /* while */ 
	delete l;

	return false;
} /* NOOSDistanceMatrix::need_to_recompute */ 


float NOOSDistanceMatrix::get_progress(void)
{
	return m_progress;
} /* NOOSDistanceMatrix::get_progress */ 


bool NOOSDistanceMatrix::is_finished(void)
{
	return m_finished;
} /* NOOSDistanceMatrix::is_finished */ 


Sort *NOOSDistanceMatrix::get_sort(void)
{
	return m_sort;
} /* NOOSDistanceMatrix::get_sort */ 


void *NOOSDistanceMatrix::launch_thread(void *p)
{
	NOOSDistanceMatrix *dm=(NOOSDistanceMatrix *)p;

	dm->thread();

	return 0;
} /* NOOSDistanceMatrix::launch_thread */ 



void NOOSDistanceMatrix::thread(void)
{
	int ncases=m_projection_cases.Length();
	int i,j;
	List<Path> projection_paths;

	/* Compute paths: */ 
	{
		List<FeatureTerm> l;
		FeatureTerm *f,*f2;

		l.Instance(m_projection_cases);
		l.Rewind();
		while(l.Iterate(f)) {
			f2=get_description(f);
			f2->get_paths(&projection_paths);
			delete f2;
		} /* while */ 
	} 

	for(i=0;i<ncases;i++) {
		for(j=i;j<ncases;j++) {
			pthread_testcancel();
			m_projection_matrix[j+i*ncases]=distance(m_projection_descriptions[i],m_projection_descriptions[j],&projection_paths);
			m_projection_matrix[j*ncases+i]=m_projection_matrix[j+i*ncases];
		} /* for */ 
		m_progress=float(i)/float(ncases);
	} /* for */ 

	m_progress=1;
	m_finished=true;

} /* NOOSDistanceMatrix::launch_thread */ 



float NOOSDistanceMatrix::distance(FeatureTerm *f1,FeatureTerm *f2,List<Path> *paths)
{
	List< List<Symbol> > l;
	List<Symbol> *path;
	Symbol *fn;
	FeatureTerm *v1,*v2;

	float s;
	float similarity=0;
	float max_similarity=float(paths->Length());

	l.Instance(*paths);
	l.Rewind();
	while(l.Iterate(path)) {
		v1=f1;
		v2=f2;
		path->Rewind();
		while(path->Iterate(fn)) {
			if (v1!=0) v1=v1->featureValueQuick(fn);
			if (v2!=0) v2=v2->featureValueQuick(fn);
		} /* while */ 

		s=simple_feature_similarity(v1,v2);
//		similarity+=s*s;
		similarity+=s;
	} /* while */ 

//	return float(sqrt(max_similarity)-sqrt(similarity));

	return (max_similarity-similarity)/max_similarity;
} /* NOOSDistanceMatrix::distance */ 



float NOOSDistanceMatrix::simple_feature_similarity(FeatureTerm *f1,FeatureTerm *f2)
{

	if (f1==0 && f2==0) {
		return 1;
	} /* if */ 
	if (f1==0 || f2==0) {
		return 0;
	} /* if */ 
	if (*f1==*f2) {
		return 1;
	} else {
		if (f1->isFloat() && f2->isFloat()) {
			float d=float(fabs(f1->getFloat()-f2->getFloat()));
			if (d>1) return 0;
				else return 1-d;
		} else {
			return 0;
		} /* if */ 
	} /* if */ 
} /* NOOSDistanceMatrix::simple_distance */ 

